##################################################################
# PyngFM is a Python implementation of Ping.fm API.
#
# Version: 1.0
# Author: Andrea Grandi <a _DOT_ grandi _AT_ gmail _DOT_ com>
# License: GPL2
# Website: http://pyngfm.googlecode.com
#
# Note: this code is inspired to PHPingFM project.
##################################################################

import urllib
import urllib2
from xml.etree import ElementTree as ET
from system_service import SystemService
from user_service import UserService
from trigger import Trigger
from message import Message

class PyngFM():
    # The developer's API key
    api_key = ''
    
    # User's app key
    user_app_key = ''
    
    def __init__(self, apikey = None, userappkey = None):
        self.api_key = apikey
        self.user_app_key = userappkey
              
    '''
    * Sets the User's APP key, if not already set.  It can also be used to change the key.
    '''
    
    def setUserAppKey(self, key):
        self.user_app_key = key
    
    '''
    * Sets the API key, if not already set.  It can also be used to change the key.
    '''
    
    def setApiKey(self, key):
        self.api_key = key
    
    '''
    * Call a method on the ping.fm server.
    * 
    * @param $service
    *   The end of the URL to fetch.  For example user.services will
    *   become http://api.ping.fm/v1/user.services when requesting from the server.
    * @param $fields
    *   The fields to pass in over POST.  The user's app key and the API key are
    *   automatically added.
    * @return TODO: document me!
    *   
    '''
    
    def __callMethod(self, service, params):
        base_url = 'http://api.ping.fm/v1/'
        url = base_url + str(service)
        
        if self.api_key != None:
            params['api_key'] = self.api_key
            
            if self.user_app_key != None:
                params['user_app_key'] = self.user_app_key
                
            parameters = urllib.urlencode(params)
            response = urllib2.urlopen(url, parameters).read()
            
            return response
    
    '''
    * Public API function: user.validate: validates the
    * given user's app key.
    * 
    * @return
    *   A boolean of whether the app key is correct.
    '''
    
    def user_validate(self):
        validates = self.__callMethod('user.validate')
        data = ET.XML(validates)
        return (data.get('status') == "OK")
    
    '''
    * Public API function: user.post: posts a message to the user's Ping.fm services.
    * 
    * @param $post_method
    *   Posting method.  Either "blog", "microblog" or "status."
    * @param $body
    *   Message body.
    * @param $title
    *   Title of the posted message.  This will only appear if the specified
    *   service supports a title field.  Otherwise, it will be discarded.
    * @param $services
    *   A single service or array of services to post to. 
    *   Default is all services set up for specified method.  If the posted
    *   method is not supported by service, the request will return an error.
    '''
    
    def user_post(self, post_method, body, title = None, services = None,
                  location = None, media = None, encoding = None, exclude = None,
                  debug = None, checksum = None):
        parameters = {}
        
        parameters['post_method'] = post_method
        parameters['body'] = body
        
        if(title != None):
            parameters['title'] = title
            
        if(services != None):
            parameters['services'] = services
            
        if(location != None):
            parameters['location'] = location
            
        if(media != None):
            parameters['media'] = media
        
        if(encoding != None):
            parameters['encoding'] = encoding
            
        if(exclude != None):
            parameters['exclude'] = exclude
            
        if(debug != None):
            parameters['debug'] = debug
            
        if(checksum != None):
            parameters['checksum'] = checksum
            
        response = self.__callMethod('user.post', parameters)
        data = ET.XML(response)
        
        return data.get('status')

    '''
    * Public API function: user.tpost: posts a message to the user's Ping.fm
    * services using one of their custom triggers.
    * 
    * @param $trigger
    *   Custom trigger the user has defined from the Ping.fm website.
    * @param $body
    *   Message body.
    * @param $title
    *   Title of the posted message.  This will only appear if the specified
    *   service supports a title field.  Otherwise, it will be discarded.
    '''
    
    def user_tpost(self, trigger, body, title = None, location = None,
                   media = None, encoding = None, exclude = None,
                    debug = None, checksum = None):
        parameters = {}
        
        parameters['trigger'] = trigger
        parameters['body'] = body
        
        if(title != None):
            parameters['title'] = title
            
        if(location != None):
            parameters['location'] = location
            
        if(media != None):
            parameters['media'] = media
        
        if(encoding != None):
            parameters['encoding'] = encoding
            
        if(exclude != None):
            parameters['exclude'] = exclude
            
        if(debug != None):
            parameters['debug'] = debug
            
        if(checksum != None):
            parameters['checksum'] = checksum
            
        response = self.__callMethod('user.tpost', parameters)
        data = ET.XML(response)
        
        return data.get('status')

    '''
    * Public API function: system.services: Return a complete list of
    * supported services.
    * 
    * @return
    *   A list of SystemService objects containing all informations about
    *   Ping.fm supported services
    '''

    def system_services(self):
        parameters = {}
        
        response = self.__callMethod('system.services', parameters)
        data = ET.XML(response)
        
        if(data.get('status') == "OK"):
            services = data.find('services')
    
            service_list = []
    
            for s in services:
                service = SystemService()
        
                service.setId(s.attrib['id'])
                service.setName(s.attrib['name'])
                service.setTrigger(s.find('trigger').text)
                service.setUrl(s.find('url').text)
                service.setIcon(s.find('icon').text)
            
                service_list.append(service)
            
            return service_list

    '''
    * Public API function: user.key: Will exchange a mobile application key for
    * a functional application key.  This is for mobile apps that would offer an
    * easier way of authenticating users.
    * 
    * @return
    *   A User Application's key
    '''
    
    def user_key(self, mobile_key):
        parameters = {}
        parameters['mobile_key'] = mobile_key
        
        response = self.__callMethod('user.key', parameters)
        data = ET.XML(response)
        
        if(data.get('status') == "OK"):
            key_el = data.find('key')
            return key_el.text
        
    '''
    * Public API function: user.services: Returns a list of services the
    * particular user has set up through Ping.fm.
    * 
    * @return
    *   A list of UserService objects containing all informations about services the
    *   particular user has set up through Ping.fm.
    '''
        
    def user_services(self):
        parameters = {}
        
        response = self.__callMethod('user.services', parameters)
        data = ET.XML(response)
        
        if(data.get('status') == "OK"):
            services = data.find('services')
    
            service_list = []
    
            for s in services:
                service = UserService()
        
                service.setId(s.attrib['id'])
                service.setName(s.attrib['name'])
                service.setTrigger(s.find('trigger').text)
                service.setUrl(s.find('url').text)
                service.setIcon(s.find('icon').text)
                service.setMethods(s.find('methods').text.split(','))
            
                service_list.append(service)
            
            return service_list

    '''
    * Public API function: user.triggers: Returns a user's custom triggers.
    * 
    * @return
    *   A list of Trigger objects containing all informations about
    *   user's custom triggers.
    '''
    
    def user_triggers(self):
        parameters = {}
        
        response = self.__callMethod('user.triggers', parameters)
        data = ET.XML(response)
        
        if(data.get('status') == "OK"):
            triggers = data.find('triggers')
    
            trigger_list = []
    
            for t in triggers:
                trigger = Trigger()
        
                trigger.setId(t.attrib['id'])
                trigger.setMethod(t.attrib['method'])
                
                services = t.find('services')
        
                for s in services:
                    trigger.addService(s.attrib['id'], s.attrib['name'])
                    
                trigger_list.append(trigger)
            
            return trigger_list

    '''
    * Public API function: user.latest: Returns the last 25 messages a
    * user has posted through Ping.fm.
    * 
    * @return
    *   A list of Message object containing the last messages a user has posted
    *   trough Ping.fm
    '''

    def user_latest(self, limit = 25, order = 'DESC'):
        parameters = {}
        parameters['limit'] = limit
        parameters['order'] = order
        
        response = self.__callMethod('user.latest', parameters)
        data = ET.XML(response)
        
        print response
        
        if(data.get('status') == "OK"):
            messages = data.find('messages')
    
            message_list = []
    
            for m in messages:
                message = Message()
        
                message.setId(m.attrib['id'])
                message.setMethod(m.attrib['method'])
                
                date = m.find('date')
                
                message.setRfcTime(date.attrib['rfc'])
                message.setUnixTime(date.attrib['unix'])
                    
                services = m.find('services')
        
                for s in services:
                    message.addService(s.attrib['id'], s.attrib['name'])
                    
                content = m.find('content')
        
                for c in content:
                    if c.tag == 'body':
                        message.setBody(c.text)
                    if c.tag == 'title':
                        message.setTitle(c.text)
                        
                location = m.find('location')
        
                if location != None:
                    message.setLocation(location.text)
                            
                message_list.append(message)
            
            return message_list
    